summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_vulkan/vk_rasterizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_vulkan/vk_rasterizer.cpp')
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp421
1 files changed, 363 insertions, 58 deletions
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ac1eb9895..242bf9602 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -180,7 +180,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
RasterizerVulkan::~RasterizerVulkan() = default;
-void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
+template <typename Func>
+void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(Vulkan_Drawing);
SCOPE_EXIT({ gpu.TickWork(); });
@@ -201,20 +202,67 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
UpdateDynamicStates();
- const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
- const u32 num_instances{instance_count};
- const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
- scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
- if (draw_params.is_indexed) {
- cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
- draw_params.first_index, draw_params.base_vertex,
- draw_params.base_instance);
- } else {
- cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
- draw_params.base_vertex, draw_params.base_instance);
+ draw_func();
+
+ EndTransformFeedback();
+}
+
+void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
+ PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
+ const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
+ const u32 num_instances{instance_count};
+ const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
+ scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
+ if (draw_params.is_indexed) {
+ cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
+ draw_params.first_index, draw_params.base_vertex,
+ draw_params.base_instance);
+ } else {
+ cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
+ draw_params.base_vertex, draw_params.base_instance);
+ }
+ });
+ });
+}
+
+void RasterizerVulkan::DrawIndirect() {
+ const auto& params = maxwell3d->draw_manager->GetIndirectParams();
+ buffer_cache.SetDrawIndirect(&params);
+ PrepareDraw(params.is_indexed, [this, &params] {
+ const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
+ const auto& buffer = indirect_buffer.first;
+ const auto& offset = indirect_buffer.second;
+ if (params.include_count) {
+ const auto count = buffer_cache.GetDrawIndirectCount();
+ const auto& draw_buffer = count.first;
+ const auto& offset_base = count.second;
+ scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
+ buffer_obj = buffer->Handle(), offset_base, offset,
+ params](vk::CommandBuffer cmdbuf) {
+ if (params.is_indexed) {
+ cmdbuf.DrawIndexedIndirectCount(
+ buffer_obj, offset, draw_buffer_obj, offset_base,
+ static_cast<u32>(params.max_draw_counts), static_cast<u32>(params.stride));
+ } else {
+ cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base,
+ static_cast<u32>(params.max_draw_counts),
+ static_cast<u32>(params.stride));
+ }
+ });
+ return;
}
+ scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
+ if (params.is_indexed) {
+ cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
+ static_cast<u32>(params.max_draw_counts),
+ static_cast<u32>(params.stride));
+ } else {
+ cmdbuf.DrawIndirect(buffer_obj, offset, static_cast<u32>(params.max_draw_counts),
+ static_cast<u32>(params.stride));
+ }
+ });
});
- EndTransformFeedback();
+ buffer_cache.SetDrawIndirect(nullptr);
}
void RasterizerVulkan::Clear(u32 layer_count) {
@@ -379,44 +427,58 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in
void RasterizerVulkan::FlushAll() {}
-void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
+void RasterizerVulkan::FlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if (addr == 0 || size == 0) {
return;
}
- {
+ if (True(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex};
texture_cache.DownloadMemory(addr, size);
}
- {
+ if ((True(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.DownloadMemory(addr, size);
}
- query_cache.FlushRegion(addr, size);
+ if ((True(which & VideoCommon::CacheType::QueryCache))) {
+ query_cache.FlushRegion(addr, size);
+ }
}
-bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
- std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
+bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
+ if ((True(which & VideoCommon::CacheType::BufferCache))) {
+ std::scoped_lock lock{buffer_cache.mutex};
+ if (buffer_cache.IsRegionGpuModified(addr, size)) {
+ return true;
+ }
+ }
if (!Settings::IsGPULevelHigh()) {
- return buffer_cache.IsRegionGpuModified(addr, size);
+ return false;
+ }
+ if (True(which & VideoCommon::CacheType::TextureCache)) {
+ std::scoped_lock lock{texture_cache.mutex};
+ return texture_cache.IsRegionGpuModified(addr, size);
}
- return texture_cache.IsRegionGpuModified(addr, size) ||
- buffer_cache.IsRegionGpuModified(addr, size);
+ return false;
}
-void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) {
+void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType which) {
if (addr == 0 || size == 0) {
return;
}
- {
+ if (True(which & VideoCommon::CacheType::TextureCache)) {
std::scoped_lock lock{texture_cache.mutex};
texture_cache.WriteMemory(addr, size);
}
- {
+ if ((True(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.WriteMemory(addr, size);
}
- pipeline_cache.InvalidateRegion(addr, size);
- query_cache.InvalidateRegion(addr, size);
+ if ((True(which & VideoCommon::CacheType::QueryCache))) {
+ query_cache.InvalidateRegion(addr, size);
+ }
+ if ((True(which & VideoCommon::CacheType::ShaderCache))) {
+ pipeline_cache.InvalidateRegion(addr, size);
+ }
}
void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
@@ -481,11 +543,12 @@ void RasterizerVulkan::ReleaseFences() {
fence_manager.WaitPendingFences();
}
-void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size,
+ VideoCommon::CacheType which) {
if (Settings::IsGPULevelExtreme()) {
- FlushRegion(addr, size);
+ FlushRegion(addr, size, which);
}
- InvalidateRegion(addr, size);
+ InvalidateRegion(addr, size, which);
}
void RasterizerVulkan::WaitForIdle() {
@@ -541,6 +604,21 @@ void RasterizerVulkan::TickFrame() {
}
}
+bool RasterizerVulkan::AccelerateConditionalRendering() {
+ if (Settings::IsGPULevelHigh()) {
+ // TODO(Blinkhawk): Reimplement Host conditional rendering.
+ return false;
+ }
+ // Medium / Low Hack: stub any checks on queries writen into the buffer cache.
+ const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
+ Maxwell::ReportSemaphore::Compare cmp;
+ if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
+ VideoCommon::CacheType::BufferCache)) {
+ return true;
+ }
+ return false;
+}
+
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -561,7 +639,7 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
}
gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
{
- std::unique_lock<std::mutex> lock{buffer_cache.mutex};
+ std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex};
if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
buffer_cache.WriteMemory(*cpu_addr, copy_size);
}
@@ -639,16 +717,35 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateLineWidth(regs);
if (device.IsExtExtendedDynamicStateSupported()) {
UpdateCullMode(regs);
- UpdateDepthBoundsTestEnable(regs);
- UpdateDepthTestEnable(regs);
- UpdateDepthWriteEnable(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
UpdateStencilOp(regs);
- UpdateStencilTestEnable(regs);
+
if (device.IsExtVertexInputDynamicStateSupported()) {
UpdateVertexInput(regs);
}
+
+ if (state_tracker.TouchStateEnable()) {
+ UpdateDepthBoundsTestEnable(regs);
+ UpdateDepthTestEnable(regs);
+ UpdateDepthWriteEnable(regs);
+ UpdateStencilTestEnable(regs);
+ if (device.IsExtExtendedDynamicState2Supported()) {
+ UpdatePrimitiveRestartEnable(regs);
+ UpdateRasterizerDiscardEnable(regs);
+ UpdateDepthBiasEnable(regs);
+ }
+ if (device.IsExtExtendedDynamicState3EnablesSupported()) {
+ UpdateLogicOpEnable(regs);
+ UpdateDepthClampEnable(regs);
+ }
+ }
+ if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
+ UpdateLogicOp(regs);
+ }
+ if (device.IsExtExtendedDynamicState3Supported()) {
+ UpdateBlending(regs);
+ }
}
}
@@ -789,32 +886,92 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
if (!state_tracker.TouchStencilProperties()) {
return;
}
- if (regs.stencil_two_side_enable) {
- // Separate values per face
- scheduler.Record(
- [front_ref = regs.stencil_front_ref, front_write_mask = regs.stencil_front_mask,
- front_test_mask = regs.stencil_front_func_mask, back_ref = regs.stencil_back_ref,
- back_write_mask = regs.stencil_back_mask,
- back_test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
+ bool update_references = state_tracker.TouchStencilReference();
+ bool update_write_mask = state_tracker.TouchStencilWriteMask();
+ bool update_compare_masks = state_tracker.TouchStencilCompare();
+ if (state_tracker.TouchStencilSide(regs.stencil_two_side_enable != 0)) {
+ update_references = true;
+ update_write_mask = true;
+ update_compare_masks = true;
+ }
+ if (update_references) {
+ [&]() {
+ if (regs.stencil_two_side_enable) {
+ if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) &&
+ !state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) {
+ return;
+ }
+ } else {
+ if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
+ return;
+ }
+ }
+ scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref,
+ two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
+ const bool set_back = two_sided && front_ref != back_ref;
// Front face
- cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT, front_ref);
- cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front_write_mask);
- cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT, front_test_mask);
-
- // Back face
- cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
- cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
- cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
+ cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
+ : VK_STENCIL_FACE_FRONT_AND_BACK,
+ front_ref);
+ if (set_back) {
+ cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
+ }
});
- } else {
- // Front face defines both faces
- scheduler.Record([ref = regs.stencil_front_ref, write_mask = regs.stencil_front_mask,
- test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
- cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
- cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
- cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask);
- });
+ }();
+ }
+ if (update_write_mask) {
+ [&]() {
+ if (regs.stencil_two_side_enable) {
+ if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) &&
+ !state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
+ return;
+ }
+ } else {
+ if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
+ return;
+ }
+ }
+ scheduler.Record([front_write_mask = regs.stencil_front_mask,
+ back_write_mask = regs.stencil_back_mask,
+ two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
+ const bool set_back = two_sided && front_write_mask != back_write_mask;
+ // Front face
+ cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
+ : VK_STENCIL_FACE_FRONT_AND_BACK,
+ front_write_mask);
+ if (set_back) {
+ cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
+ }
+ });
+ }();
+ }
+ if (update_compare_masks) {
+ [&]() {
+ if (regs.stencil_two_side_enable) {
+ if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) &&
+ !state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
+ return;
+ }
+ } else {
+ if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
+ return;
+ }
+ }
+ scheduler.Record([front_test_mask = regs.stencil_front_func_mask,
+ back_test_mask = regs.stencil_back_func_mask,
+ two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
+ const bool set_back = two_sided && front_test_mask != back_test_mask;
+ // Front face
+ cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
+ : VK_STENCIL_FACE_FRONT_AND_BACK,
+ front_test_mask);
+ if (set_back) {
+ cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
+ }
+ });
+ }();
}
+ state_tracker.ClearStencilReset();
}
void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -868,6 +1025,82 @@ void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& r
});
}
+void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchPrimitiveRestartEnable()) {
+ return;
+ }
+ scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetPrimitiveRestartEnableEXT(enable);
+ });
+}
+
+void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchRasterizerDiscardEnable()) {
+ return;
+ }
+ scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
+ });
+}
+
+void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchDepthBiasEnable()) {
+ return;
+ }
+ constexpr size_t POINT = 0;
+ constexpr size_t LINE = 1;
+ constexpr size_t POLYGON = 2;
+ static constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
+ POINT, // Points
+ LINE, // Lines
+ LINE, // LineLoop
+ LINE, // LineStrip
+ POLYGON, // Triangles
+ POLYGON, // TriangleStrip
+ POLYGON, // TriangleFan
+ POLYGON, // Quads
+ POLYGON, // QuadStrip
+ POLYGON, // Polygon
+ LINE, // LinesAdjacency
+ LINE, // LineStripAdjacency
+ POLYGON, // TrianglesAdjacency
+ POLYGON, // TriangleStripAdjacency
+ POLYGON, // Patches
+ };
+ const std::array enabled_lut{
+ regs.polygon_offset_point_enable,
+ regs.polygon_offset_line_enable,
+ regs.polygon_offset_fill_enable,
+ };
+ const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
+ const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
+ scheduler.Record(
+ [enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
+}
+
+void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchLogicOpEnable()) {
+ return;
+ }
+ scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetLogicOpEnableEXT(enable != 0);
+ });
+}
+
+void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchDepthClampEnable()) {
+ return;
+ }
+ bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
+ Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
+ regs.viewport_clip_control.geometry_clip ==
+ Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
+ regs.viewport_clip_control.geometry_clip ==
+ Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
+ scheduler.Record(
+ [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
+}
+
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthCompareOp()) {
return;
@@ -925,6 +1158,78 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
}
}
+void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchLogicOp()) {
+ return;
+ }
+ const auto op_value = static_cast<u32>(regs.logic_op.op);
+ auto op = op_value >= 0x1500 && op_value < 0x1510 ? static_cast<VkLogicOp>(op_value - 0x1500)
+ : VK_LOGIC_OP_NO_OP;
+ scheduler.Record([op](vk::CommandBuffer cmdbuf) { cmdbuf.SetLogicOpEXT(op); });
+}
+
+void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
+ if (!state_tracker.TouchBlending()) {
+ return;
+ }
+
+ if (state_tracker.TouchColorMask()) {
+ std::array<VkColorComponentFlags, Maxwell::NumRenderTargets> setup_masks{};
+ for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
+ const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
+ auto& current = setup_masks[index];
+ if (mask.R) {
+ current |= VK_COLOR_COMPONENT_R_BIT;
+ }
+ if (mask.G) {
+ current |= VK_COLOR_COMPONENT_G_BIT;
+ }
+ if (mask.B) {
+ current |= VK_COLOR_COMPONENT_B_BIT;
+ }
+ if (mask.A) {
+ current |= VK_COLOR_COMPONENT_A_BIT;
+ }
+ }
+ scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetColorWriteMaskEXT(0, setup_masks);
+ });
+ }
+
+ if (state_tracker.TouchBlendEnable()) {
+ std::array<VkBool32, Maxwell::NumRenderTargets> setup_enables{};
+ std::ranges::transform(
+ regs.blend.enable, setup_enables.begin(),
+ [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; });
+ scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetColorBlendEnableEXT(0, setup_enables);
+ });
+ }
+
+ if (state_tracker.TouchBlendEquations()) {
+ std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
+ for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
+ const auto blend_setup = [&]<typename T>(const T& guest_blend) {
+ auto& host_blend = setup_blends[index];
+ host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
+ host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
+ host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
+ host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
+ host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
+ host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
+ };
+ if (!regs.blend_per_target_enabled) {
+ blend_setup(regs.blend);
+ continue;
+ }
+ blend_setup(regs.blend_per_target[index]);
+ }
+ scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
+ });
+ }
+}
+
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchStencilTestEnable()) {
return;